﻿#include "stdafx.h"

#include "svgshape.h"

#include "utils.h"

#include <QFontMetricsF>

namespace
{
	double _LineSlope(const QLineF& line)
	{
		return line.dy() / line.dx();
	}

	// 这个代码是完全抄 https://github.com/methodofaction/Method-Draw
	std::tuple<QPointF, QPointF> _SmoothControlPoints(const QPointF& ct1, const QPointF& ct2, const QPointF& pt)
	{
		// each point must not be the origin
		const double x1 = ct1.x() - pt.x();
		const double y1 = ct1.y() - pt.y();
		const double x2 = ct2.x() - pt.x();
		const double y2 = ct2.y() - pt.y();

		if ((x1 != 0 || y1 != 0) && (x2 != 0 || y2 != 0))
		{
			double angleA = atan2(y1, x1);
			double angleB = atan2(y2, x2);
			const double r1 = sqrt(x1 * x1 + y1 * y1);
			const double r2 = sqrt(x2 * x2 + y2 * y2);
			QPointF nct1;
			QPointF nct2;
			if (angleA < 0) { angleA += 2 * M_PI; }
			if (angleB < 0) { angleB += 2 * M_PI; }

			const double angleBetween = abs(angleA - angleB);
			const double angleDiff = abs(M_PI - angleBetween) / 2;

			double newAngleA = 0;
			double newAngleB = 0;
			if (angleA - angleB > 0)
			{
				newAngleA = angleBetween < M_PI ? (angleA + angleDiff) : (angleA - angleDiff);
				newAngleB = angleBetween < M_PI ? (angleB - angleDiff) : (angleB + angleDiff);
			}
			else
			{
				newAngleA = angleBetween < M_PI ? (angleA - angleDiff) : (angleA + angleDiff);
				newAngleB = angleBetween < M_PI ? (angleB + angleDiff) : (angleB - angleDiff);
			}

			// rotate the points
			nct1.setX(r1 * cos(newAngleA) + pt.x());
			nct1.setY(r1 * sin(newAngleA) + pt.y());
			nct2.setX(r2 * cos(newAngleB) + pt.x());
			nct2.setY(r2 * sin(newAngleB) + pt.y());

			return{ nct1, nct2 };
		}
		return { {NAN, NAN}, {NAN, NAN} };
	}
}

SvgShape::SvgShape()
{
	m_pen.setWidth(2);
	m_brush.setStyle(Qt::SolidPattern);
}

QRectF SvgShape::VisualRect() const
{
	const double distance = m_pen.width() * 0.75 + 1;
	return EnlargeRect(BoundingRect(), distance);
}

void SvgLine::Paint(QPainter& painter) const
{
	// 长度为0的线段，svg标准是不绘制的，qt的 drawLine 会绘制一个点，直接跳过
	if (m_line.p1() == m_line.p2() || m_pen.widthF() == 0)
		return;
	painter.setPen(m_pen);
	painter.setBrush(m_brush);
	painter.drawLine(m_line);
}

QRectF SvgLine::BoundingRect() const
{
	return CreateRectF(m_line.p1(), m_line.p2());
}

bool SvgLine::PointInShape(const QPointF& pos) const
{
	// todo, 需要一种更好的方法
	const double delta = m_pen.widthF() / 4.0;
	return abs(m_line.length() - (QLineF(m_line.p1(), pos).length() + QLineF(pos, m_line.p2()).length())) <= delta;
}

void SvgLine::Translate(const QPointF& p)
{
	m_line.translate(p);
}

void SvgLine::Accept(ShapeVisitor<void>& visitor)
{
	visitor._VisitLine(*this);
}

void SvgLine::Translate(const QRectF& rect)
{
	if (_LineSlope(m_line) > 0)
	{
		m_line.setP1(rect.topLeft());
		m_line.setP2(rect.bottomRight());
	}
	else
	{
		m_line.setP1(rect.topRight());
		m_line.setP2(rect.bottomLeft());
	}
}

unique_ptr<SvgShape> SvgLine::Clone()
{
	return std::make_unique<SvgLine>(*this);
}

void SvgRect::Paint(QPainter& painter) const
{
	painter.setPen(m_pen);
	painter.setBrush(m_brush);
	painter.drawRect(m_rect);
}

QRectF SvgRect::BoundingRect() const
{
	return m_rect;
}

bool SvgRect::PointInShape(const QPointF& pos) const
{
	return VisualRect().contains(pos);
}

void SvgRect::Translate(const QPointF& p)
{
	m_rect.translate(p);
}

void SvgRect::Accept(ShapeVisitor<void>& visitor)
{
	visitor._VisitRect(*this);
}

void SvgRect::Translate(const QRectF& rect)
{
	ASSERT(rect.width() != 0 && rect.height() != 0);
	m_rect = CreateRectF(rect.topRight(), rect.bottomLeft());
}

unique_ptr<SvgShape> SvgRect::Clone()
{
	return std::make_unique<SvgRect>(*this);
}

SvgEllipse SvgEllipse::Circle(const QPointF& center, double radius)
{
	ASSERT(radius > 0);
	return SvgEllipse(QRectF(
		center.x() - radius, center.y() - radius, radius * 2, radius * 2
	));
}

void SvgPolygon::Paint(QPainter& painter) const
{
	painter.setPen(m_pen);
	painter.setBrush(m_brush);
	painter.drawPolygon(m_polygon);
}

QRectF SvgPolygon::BoundingRect() const
{
	return m_polygon.boundingRect();
}

bool SvgPolygon::PointInShape(const QPointF& p) const
{
	return m_polygon.containsPoint(p, FILL_RULE);
}

void SvgPolygon::Translate(const QPointF& p)
{
	m_polygon.translate(p);
}

void SvgPolygon::Accept(ShapeVisitor<void>& visitor)
{
	visitor._VisitPolygon(*this);
}

void SvgPolygon::Translate(const QRectF& rect)
{
	const QRectF old = BoundingRect();
	ASSERT(rect.width() != 0 && rect.height() != 0 && old.width() != 0 && old.height() != 0);
	const double widthScale = rect.width() / old.width();
	const double heightScale = rect.height() / old.height();

	const double oldLeft = old.left();
	const double oldTop = old.top();
	const double newLeft = rect.left();
	const double newTop = rect.top();

	for (auto& p : m_polygon)
	{
		p.setX(newLeft + (p.x() - oldLeft) * widthScale);
		p.setY(newTop + (p.y() - oldTop) * heightScale);
	}
}

unique_ptr<SvgShape> SvgPolygon::Clone()
{
	return std::make_unique<SvgPolygon>(*this);
}

// 这个代码是完全抄 https://github.com/methodofaction/Method-Draw
unique_ptr<SvgPath> SvgPath::PointsToPath(const std::vector<QPointF>& points)
{
	const int n = points.size();
	if (n <= 1)
		return nullptr;
	QPainterPath path;
	QPointF curpos = points[0];
	path.moveTo(curpos);
	int i = 1;
	if (n >= 4)
	{
		// loop through every 3 points and convert to a cubic bezier curve segment
		// 
		// NOTE: this is cheating, it means that every 3 points has the potential to 
		// be a corner instead of treating each point in an equal manner.  In general,
		// this technique does not look that good.
		// 
		// I am open to better ideas!
		// 
		// Reading:
		// - http://www.efg2.com/Lab/Graphics/Jean-YvesQueinecBezierCurves.htm
		// - http://www.codeproject.com/KB/graphics/BezierSpline.aspx?msg=2956963
		// - http://www.ian-ko.com/ET_GeoWizards/UserGuide/smooth.htm
		// - http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html
		QPointF prevCtlPt(NAN, NAN);
		for (; i <= (n - 4); i += 3)
		{
			QPointF ct1 = points[i];
			QPointF ct2 = points[i + 1];
			QPointF end = points[i + 2];

			// if the previous segment had a control point, we want to smooth out
			// the control points on both sides
			if (!std::isnan(prevCtlPt.x()))
			{
				auto newpts = _SmoothControlPoints(prevCtlPt, ct1, curpos);
				const QPointF& nct1 = std::get<0>(newpts);
				const QPointF& nct2 = std::get<1>(newpts);
				if (!std::isnan(nct1.x()))
				{
					path.setElementPositionAt(path.elementCount() - 2, nct1.x(), nct1.y());
					ct1 = nct2;
				}
			}

			path.cubicTo(ct1, ct2, end);

			curpos = end;
			prevCtlPt = ct2;
		}
		// handle remaining line segments
	}
	for (; i < n; ++i)
		path.lineTo(points[i]);
	return std::make_unique<SvgPath>(std::move(path));
}

void SvgPath::Paint(QPainter& painter) const
{
	painter.setPen(m_pen);
	painter.setBrush(m_brush);
	painter.drawPath(m_path);
}

QRectF SvgPath::BoundingRect() const
{
	return m_path.boundingRect();
}

bool SvgPath::PointInShape(const QPointF& p) const
{
	const QRectF rect(p, QSizeF(1.0, 1.0));
	return m_path.intersects(rect);
}

void SvgPath::Translate(const QPointF& p)
{
	m_path.translate(p);
}

void SvgPath::Accept(ShapeVisitor<void>& visitor)
{
	visitor._VisitPath(*this);
}

void SvgPath::Translate(const QRectF& rect)
{
	const QRectF old = BoundingRect();
	if (old.height() == 0)
	{
		m_path = QPainterPath();
		m_path.moveTo(rect.topLeft());
		m_path.lineTo(rect.bottomRight());
		return;
	}
	if (old.width() == 0)
	{
		m_path = QPainterPath();
		m_path.moveTo(rect.bottomLeft());
		m_path.lineTo(rect.topRight());
		return;
	}
	ASSERT(rect.width() != 0 && rect.height() != 0 && old.width() != 0 && old.height() != 0);
	const double widthScale = rect.width() / old.width();
	const double heightScale = rect.height() / old.height();

	const double oldLeft = old.left();
	const double oldTop = old.top();
	const double newLeft = rect.left();
	const double newTop = rect.top();

	for (int i = 0; i < m_path.elementCount(); ++i)
	{
		const QPainterPath::Element elem = m_path.elementAt(i);
		const double x = newLeft + (elem.x - oldLeft) * widthScale;
		const double y = newTop + (elem.y - oldTop) * heightScale;
		m_path.setElementPositionAt(i, x, y);
	}
}

unique_ptr<SvgShape> SvgPath::Clone()
{
	return std::make_unique<SvgPath>(*this);
}

QSizeF SvgText::ContentSize(const QString& content, const QFont& font)
{
	const QFontMetricsF metrics(font);
	return metrics.size(Qt::TextSingleLine, content);
}

void SvgText::Paint(QPainter& painter) const
{
	painter.setPen(m_pen);
	painter.setBrush(m_brush);
	painter.setFont(m_font);
	painter.drawText(m_pos, m_text);
}

QRectF SvgText::BoundingRect() const
{
	// size 可以考虑缓存一下，反正只在 setText 的时候变化
	const QSizeF size = ContentSize(m_text, m_font);
	return { QPointF(m_pos.x(), m_pos.y() - size.height() * 0.8), size };
}

bool SvgText::PointInShape(const QPointF& p) const
{
	return BoundingRect().contains(p);
}

void SvgText::Translate(const QPointF& p)
{
	m_pos += p;
}

void SvgText::Accept(ShapeVisitor<void>& visitor)
{
	visitor._VisitText(*this);
}

void SvgText::Translate(const QRectF& rect)
{
	ASSERT(rect.width() != 0 && rect.height() != 0);
	// todo: 行为不正确
	if (rect.height() >= 1)
		m_font.setPixelSize(rect.height());
}

unique_ptr<SvgShape> SvgText::Clone()
{
	return std::make_unique<SvgText>(*this);
}

void SvgEllipse::Paint(QPainter& painter) const
{
	painter.setPen(m_pen);
	painter.setBrush(m_brush);
	painter.drawEllipse(m_rect);
}

QRectF SvgEllipse::BoundingRect() const
{
	return m_rect;
}

bool SvgEllipse::PointInShape(const QPointF& p) const
{
	const QPointF c = Center();
	return pow((p.x() - c.x()) / (1 + Rx()), 2)
		+ pow((p.y() - c.y()) / (1 + Ry()), 2) <= 1;
}

void SvgEllipse::Translate(const QPointF& p)
{
	m_rect.translate(p);
}

void SvgEllipse::Accept(ShapeVisitor<void>& visitor)
{
	visitor._VisitEllipse(*this);
}

void SvgEllipse::Translate(const QRectF& rect)
{
	ASSERT(rect.width() != 0 && rect.height() != 0);
	m_rect = CreateRectF(rect.topRight(), rect.bottomLeft());
}

unique_ptr<SvgShape> SvgEllipse::Clone()
{
	return std::make_unique<SvgEllipse>(*this);
}

